package com.ruoyi.web.dataAccept;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ResourceLeakDetector;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * AIS基站数据接收服务端（带重连机制）
 */
@Slf4j
@Service("aisTcpServer")
public class HbTcpServer {

    @Value("${tcp.hb.tcpserver.ip}")
    private String ip;
    @Value("${tcp.hb.tcpserver.port}")
    private int port;

    @Autowired
    private HbDataHandler aisDataHandler;
    // 重连间隔时间（毫秒），指数退避
    private static final int RECONNECT_DELAY = 1000;
    private static final int MAX_RECONNECT_DELAY = 60000;


    // 线程池
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    private int reconnectCount = 0;

    /**
     * 启动AIS服务端接收程序
     */
    public void start(){
        // 确保线程安全的启动
        if (!isRunning.compareAndSet(false, true)) {
            log.info("AIS TCP服务器已在运行中");
            return;
        }

        // 重置重连计数
        reconnectCount = 0;
        doStart();
    }

    /**
     * 实际启动服务器的方法
     */
    private void doStart() {
        // 接收客户端连接的线程池
        bossGroup = new NioEventLoopGroup(4);
        // 工作线程
        workerGroup = new NioEventLoopGroup(8);

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class);

            // 配置服务器选项
            bootstrap.option(ChannelOption.SO_REUSEADDR, true)
                .option(ChannelOption.SO_BACKLOG, 1024 * 4)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

            // 配置客户端连接选项
            bootstrap.childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childOption(ChannelOption.SO_LINGER, 0);

            // 设置处理器
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    channel.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
                    channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                    channel.pipeline().addLast(new StringEncoder());
                    channel.pipeline().addLast(new StringDecoder());
                    channel.pipeline().addLast(aisDataHandler);
                }
            });

            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);

            // 绑定端口并启动服务
            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("AIS数据服务端启动成功，正在监听{}端口", port);

            // 保存服务器通道引用
            serverChannel = future.channel();

            // 监听通道关闭事件，实现自动重连
            future.channel().closeFuture().addListener((ChannelFutureListener) future1 -> {
                log.warn("AIS TCP服务器通道已关闭，准备重连");
                scheduleReconnect();
            });

        } catch (Exception e) {
            log.error("AIS数据服务端启动失败", e);
            // 启动失败时进行资源清理
            shutdown();
            // 安排重连
            scheduleReconnect();
        }
    }

    /**
     * 安排重连任务，使用指数退避策略
     */
    private void scheduleReconnect() {
        if (!isRunning.get()) {
            log.info("服务器已停止，不进行重连");
            return;
        }

        // 计算重连延迟，指数退避，最多60秒
        int delay = Math.min(RECONNECT_DELAY * (1 << reconnectCount), MAX_RECONNECT_DELAY);
        reconnectCount++;

        log.info("将在{}毫秒后尝试第{}次重连", delay, reconnectCount);

        scheduler.schedule(() -> {
            log.info("尝试重连AIS TCP服务器...");
            doStart();
        }, delay, TimeUnit.MILLISECONDS);
    }

    /**
     * 优雅关闭服务器
     */
    public void shutdown() {
        isRunning.set(false);

        // 关闭服务器通道
        if (serverChannel != null && serverChannel.isOpen()) {
            try {
                serverChannel.close().sync();
            } catch (InterruptedException e) {
                log.error("关闭服务器通道时发生异常", e);
                Thread.currentThread().interrupt();
            }
        }

        // 关闭事件循环组
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }

        log.info("AIS TCP服务器已关闭");
    }

    // 初始化连接池
    @PostConstruct
    public void initConnections() {
        log.info("AIS数据服务端开始启动，准备监听{}端口", port);
        // 启动AIS数据服务端
        start();
    }

    // 应用关闭时关闭服务器
    @PreDestroy
    public void destroy() {
        shutdown();
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
        }
        log.info("AIS数据服务端已销毁");
    }

    // 监控连接池状态
    private void connectStatus() {
        // 检查服务器状态，如果未运行则触发重连
        if (!isRunning.get() || (serverChannel != null && !serverChannel.isActive())) {
            log.warn("检测到服务器连接异常，触发重连机制");
            shutdown();
            start();
        }
    }
}
